package com.idealighter.game.games.fruitslot.manager;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;

import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.chat.manager.ChatMsgMgr;
import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.BoolInteger;
import com.idealighter.game.core.constant.Operator;
import com.idealighter.game.core.constant.notice.Notice;
import com.idealighter.game.core.constant.notice.NoticeType;
import com.idealighter.game.core.constant.room.RoomActiveConstant;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.GameNoticeDic;
// import com.idealighter.game.dictionary.domain.FruitslotBonusIconDomain;
import com.idealighter.game.dictionary.domain.FruitslotRoomDomain;
import com.idealighter.game.dictionary.domwrapper.GameNoticeDomainWrapper;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.GameClearExitEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownGameEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartGameEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.dto.MemInfo;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.InstanceState;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.fruitslot.common.FruitslotReward;
import com.idealighter.game.games.fruitslot.common.FruitslotRewardType;
import com.idealighter.game.games.fruitslot.struct.FruitslotRoom;
import com.idealighter.game.games.fruitslot.struct.FruitslotSeat;
import com.idealighter.game.games.fruitslot.struct.FruitslotTable;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game23WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.message.core.ResMessage;
import com.idealighter.game.notice.manager.NoticeMsgMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.utils.check.EmptyUtil;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 水果拉霸 .
 * 
 * @date 2015年9月25日 下午2:21:14
 *
 */
@Singleton
public class FruitslotMgr {

  private static final Logger LOG = LoggerFactory.getLogger(FruitslotMgr.class);


  public static final int MAX_LINE = 9;

  public static final int NOTICE_FOR_MULTIPLE = 80;

  private final EventMgr eventMgr;
  @Inject
  private CommonMsgMgr commonMsgMgr;
  @Inject
  private GameNoticeDic gameNoticeDic;
  @Inject
  private NoticeMsgMgr noticeMsgMgr;
  @Inject
  private ChatMsgMgr chatMsgMgr;
  @Inject
  private FruitslotDataMgr dataMgr;
  @Inject
  private FruitslotMsgMgr msgMgr;
  @Inject
  private PlayerMgr playerMgr;

  // @Inject
  // private FruitslotBonusIconDic bonusIconDic;
  @Inject
  private FruitslotControlScript fruitslotControlMgr;
  @Inject
  private LoginMgr loginMgr;

  // 游戏线程executor,可以执行普通任务和定时任务
  private final DisruptorExecutor gameExecutor;

  /**
   * 构造函数.
   * 
   * @param eventMgr 事件管理.
   * @param executorMgr 执行管理.
   */
  @Inject
  public FruitslotMgr(EventMgr eventMgr, ExecutorMgr executorMgr) {
    this.eventMgr = eventMgr;
    eventMgr.register(this);
    this.gameExecutor = executorMgr.getGameExecutor(Game.FRUIT_SLOT.getModuleId());
  }

  private void clearLogoutPlayer(Player player) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    if (seat != null) {
      // 结算
      bill(player, null, true);
      // 玩家在房间中退出桌子
      doExitTabl(player, seat);
    }

    // 玩家是否在房间中
    if (player.curRoom instanceof FruitslotRoom) {
      // 退出房间
      doExitRoom(player);
    }

    LOG.info("执行用户{}在[水果拉霸]游戏中退出退出，并通知游戏清空用户", player.getId());

    GameClearExitEvent gameClearExitEvent =
        new GameClearExitEvent(player, Game.FRUIT_SLOT.getDesc());
    eventMgr.post(gameClearExitEvent);
  }

  /**
   * 检验是否房间关闭，如果桌子不在游戏中或者游戏正在休息，顺便把桌子关闭 .
   *
   * @author abin
   * @date 2018年4月28日 下午3:41:20
   * @param table 桌子.
   */
  private boolean checkClose(FruitslotTable table) {
    boolean remove = false;
    if (table.getRoom().getInstanceState() == InstanceState.TO_REMOVE) {
      boolean hasBonus = false;
      for (Long playerId : table.players()) {
        FruitslotSeat seat = dataMgr.getPlayerSeat(playerId);
        if (seat != null && (seat.getBonus() > 0
            || EmptyUtil.listIsNotEmpty(seat.getLotteryRewards()) || seat.isGaming())) {
          hasBonus = true;
        }
      }
      if (!hasBonus) {
        for (Long playerId : table.players()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            kickoutForRoomClose(player);
          }
        }
        FruitslotRoom room = table.getRoom();
        room.removeTable(table.getId().intValue());

        table = null;
        remove = true;
      }

    }
    return remove;
  }

  private void deleteRooms(List<FruitslotRoom> deleteRooms) {
    if (EmptyUtil.listIsNotEmpty(deleteRooms)) {
      for (Iterator<FruitslotRoom> iterator = deleteRooms.iterator(); iterator.hasNext();) {
        FruitslotRoom fruitslotRoom = iterator.next();
        List<FruitslotTable> tables = new ArrayList<>(fruitslotRoom.tables());
        for (Iterator<FruitslotTable> tableIterator = tables.iterator(); tableIterator.hasNext();) {
          FruitslotTable fruitslotTable = tableIterator.next();
          checkClose(fruitslotTable);
        }
        dataMgr.checkRemoveInstance(fruitslotRoom.getId().intValue(),
            fruitslotRoom.getInstanceId());
      }
    }
  }

  /**
   * 玩家离开监听.
   * 
   * @param event 玩家离开事件.
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {

    gameExecutor.execute(() -> {
      Player player = event.player;
      if (player.curRoom instanceof FruitslotRoom) {
        LOG.info("捕获到用户{}退出", player.getId());
        clearLogoutPlayer(player);
      }
    });
  }

  /**
   * 房间开启事件监听.
   * 
   * @param event 房间开启事件.
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FRUIT_SLOT) {
        List<FruitslotRoom> deleteRooms = dataMgr.startRoom(event.getRoomId(), true);
        deleteRooms(deleteRooms);
      }
    });
  }

  /**
   * 关闭房间事件监听.
   * 
   * @param event 事件.
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FRUIT_SLOT) {
        List<FruitslotRoom> deleteRooms = dataMgr.deleteRoom(event.getRoomId());
        deleteRooms(deleteRooms);
      }

    });
  }

  /**
   * 开启游戏 .
   *
   * @author abin
   * @date 2018年5月18日 上午11:24:51
   * @param event 开启游戏事件.
   */
  @Subscribe
  public void onStartGame(StartGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FRUIT_SLOT) {
        List<Integer> ids = dataMgr.reloadRoom();
        if (EmptyUtil.listIsNotEmpty(ids)) {
          for (Integer id : ids) {
            List<FruitslotRoom> deleteRooms = dataMgr.startRoom(id, false);
            deleteRooms(deleteRooms);
          }
        }
      }
    });
  }

  /**
   * 关闭游戏 .
   *
   * @author abin
   * @date 2018年5月18日 上午11:24:51
   * @param event 关闭游戏事件.
   */
  @Subscribe
  public void onShutdownGame(ShutdownGameEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.FRUIT_SLOT) {
        Collection<FruitslotRoom> rooms = dataMgr.allRooms();
        if (!EmptyUtil.isEmpty(rooms)) {
          for (FruitslotRoom baccaratRoom : rooms) {
            List<FruitslotRoom> deleteRooms = dataMgr.deleteRoom(baccaratRoom.getId().intValue());
            deleteRooms(deleteRooms);
          }
        }

      }
    });
  }


  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 玩家进入水果拉霸大厅 .
   * 
   * @param player 玩家.
   */
  public ResMessage enterGameHall(Player player) {
    // 不应该已经进入游戏中.
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[水果拉霸]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ALREADY_IN, game.getDesc());
    }

    LOG.info("[水果拉霸]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());

    return msgMgr.resEnterGameHallMsg();
  }

  /**
   * 玩家进入房间 .
   * 
   * @param player 玩家.
   * @param roomId 房间Id.
   * @return res 消息.
   */
  public ResMessage enterRoom(Player player, int roomId) {
    AbstractRoom curRoom = player.curRoom;

    HuohuaAssert.isTrue(curRoom == null);

    // if (curRoom != null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getId(), player.getPlayerName(),
    // curRoom.game(), curRoom.name);
    // return;
    // }

    FruitslotRoom room = dataMgr.getNewestRoom(roomId);

    HuohuaAssert.isTrue(room != null && room.getInstanceState().equals(InstanceState.NORMAL),
        ErrorCode.GAME_ROOM_CLOSED);

    // if (room == null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
    // return;
    // }

    FruitslotRoomDomain roomDom = room.getRoomDomain();

    HuohuaAssert.isTrue(roomDom.getIsActive() == RoomActiveConstant.ACTIVE);

    // if (roomDom.getIsActive() != RoomActiveConstant.ACTIVE) {
    // LOG.error("[水果拉霸]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
    // return;
    // }
    HuohuaAssert.isTrue(player.getGold() >= roomDom.getLower(), ErrorCode.GAME_BELOW_MIN_GOLD);
    // if (player.getGold() < roomDom.getLower()) {
    // commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BELOW_MIN_GOLD, Operator.SYSTEM,
    // "" + roomDom.getLower());
    // LOG.error("[水果拉霸]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
    // roomId, roomDom.getLower());
    // return;
    // }

    HuohuaAssert.isTrue(player.getGold() <= roomDom.getUpper(), ErrorCode.GAME_BEYOND_MAX_GOLD);
    // if (player.getGold() > roomDom.getUpper()) {
    // commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BEYOND_MAX_GOLD, Operator.SYSTEM,
    // "" + roomDom.getUpper());
    // LOG.error("[水果拉霸]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
    // roomId, roomDom.getUpper());
    // return;
    // }

    if (!player.vip()
        && room.getPlayers().size() >= roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
      // commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_VIP_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[水果拉霸]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_VIP_ROOM_FULL);
    }

    HuohuaAssert.isTrue(room.getPlayers().size() < roomDom.getMaxNum(), ErrorCode.GAME_ROOM_FULL);

    // if (room.players.size() >= roomDom.getMaxNum()) {
    // commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ROOM_FULL, Operator.SYSTEM);
    // LOG.warn("[水果拉霸]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(),
    // roomId, roomDom.getMaxNum());
    // return;
    // }

    // 修改玩家当前房间
    player.curRoom = room;
    room.getPlayers().add(player.getId());


    LOG.info("[水果拉霸]玩家[{}][{}]进入的房间[{}]成功", player.getPlayerName(), player.getId(), roomId);
    // 触发进入房间事件
    eventMgr.post(new EnterRoomEvent(player, room));

    // 发送进入房间消息
    return msgMgr.resEnterRoomMsg(player);
  }

  /**
   * 玩家金币兑换筹码.
   * 
   * @param player 玩家.
   * @param gold 兑换筹码的金币.
   */
  public void exchangeChips(Player player, long gold) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[水果拉霸]玩家[{}][{}]不在牌桌中不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    if (player.getGold() < gold
        + convertGold(seat.getTable().getRoom().getRoomDomain(), seat.getTotalChips())) {
      LOG.error("[水果拉霸]玩家[{}][{}]兑换的金币[{}]加上筹码[{}]兑换的金币超过自己的金币总和[{}]", player.getPlayerName(),
          player.getId(), gold, seat.getTotalChips(), player.getGold());
      return;
    }

    if (seat.isGaming()) {
      LOG.error("[水果拉霸]玩家[{}][{}]已经开始游戏不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    long chips = convertChip(seat.getTable().getRoom().getRoomDomain(), gold);
    seat.setTotalChips(seat.getTotalChips() + chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
    // 发送兑换筹码结果消息(前端要求)
    msgMgr.sendExchangeChipsMsg(seat);
    LOG.info("[水果拉霸]玩家[{}][{}]用金币[{}]兑换筹码[{}]成功", player.getPlayerName(), player.getId(), gold,
        chips);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 玩家筹码兑换金币 .
   * 
   * @param player 玩家.
   * @param chips 筹码.
   */
  public void exchangeGold(Player player, long chips) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[水果拉霸]玩家[{}][{}]不在牌桌中不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }

    if (seat.getTotalChips() < chips) {
      LOG.error("[水果拉霸]玩家[{}][{}]兑换的筹码[{}]超过自己的筹码总和[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      return;
    }

    if (seat.isGaming()) {
      LOG.error("[水果拉霸]玩家[{}][{}]已经开始游戏不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }

    long gold = convertGold(seat.getTable().getRoom().getRoomDomain(), chips);
    minusChips(seat, chips);
    LOG.info("[水果拉霸]玩家[{}][{}]用筹码[{}]兑换金币[{}]成功", player.getPlayerName(), player.getId(), chips,
        gold);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 筹码换算金币 .
   * 
   * @param roomDom 房间Id.
   * @param chips 筹码.
   */
  public long convertGold(FruitslotRoomDomain roomDom, long chips) {
    return chips * roomDom.getProportionGold() / roomDom.getProportionChips();
  }

  /**
   * 金币换算筹码 .
   * 
   * @param roomId 房间Id.
   * @param gold 金币.
   * @return 筹码.
   */
  private long convertChip(FruitslotRoomDomain roomDom, long gold) {
    return gold * roomDom.getProportionChips() / roomDom.getProportionGold();
  }

  private void doExitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(curRoom instanceof FruitslotRoom);

    HuohuaAssert.isTrue(seat == null, ErrorCode.GAME_EXIT_ROOM_EXIT_SIT_FIRST);

    player.curRoom = null;
    FruitslotRoom room = (FruitslotRoom) curRoom;
    room.getPlayers().remove(player.getId());
    LOG.info("[水果拉霸]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
    // 触发退出房间事件
    eventMgr.post(new ExitRoomEvent(player, curRoom));
  }

  /**
   * 玩家退出房间.
   * 
   * @param player 玩家.
   */
  public ResMessage resExitRoom(Player player) {
    doExitRoom(player);
    return ResMessage.DEFAULT;
  }

  private void kickoutForRoomClose(Player player) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat != null) {
      doExitTabl(player, seat);
    }
    if (player.curRoom != null) {
      doExitRoom(player);
      commonMsgMgr.noticeRoomCloseKickout(player);
    }

  }

  /**
   * 玩家进入牌桌.
   * 
   * @param player 玩家.
   * @param tableId 桌子Id.
   */
  public ResMessage enterTable(Player player, int tableId) {

    HuohuaAssert.isTrue(player.curRoom instanceof FruitslotRoom);


    FruitslotTable table = dataMgr.getTable(tableId);

    HuohuaAssert.isTrue(table != null, ErrorCode.GAME_ROOM_CLOSED);

    HuohuaAssert.isTrue(dataMgr.getPlayerSeat(player.getId()) == null);


    FruitslotSeat seat = table.getSeat();

    HuohuaAssert.isTrue(seat.getPlayerId() <= 0, ErrorCode.GAME_SET_NOT_EMPTY);

    // 进入座位
    List<MemInfo> memInfos = doEnterTable(player, seat);

    return msgMgr.resEnterTableMsg(memInfos);
  }

  /**
   * 玩家进入座位,玩家每次进入水果拉霸桌子扣台费(金币) .
   * 
   * @param player 玩家.
   * @param seat 座位.
   */
  private List<MemInfo> doEnterTable(Player player, FruitslotSeat seat) {
    FruitslotTable table = seat.getTable();
    FruitslotRoomDomain roomDom = table.getRoom().getRoomDomain();

    HuohuaAssert.isTrue(player.getGold() >= roomDom.getLower(), ErrorCode.PLAYER_GOLD_NOT_ENOUGH);


    HuohuaAssert.isTrue(player.getGold() <= roomDom.getUpper(),
        ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD);

    // 台费(金币)
    int afee = roomDom.getAfee();

    HuohuaAssert.isTrue(player.getGold() >= afee, ErrorCode.GAME_NOT_ENOUGH_FEE);

    if (afee > 0) {
      playerMgr.minusGold(player, afee, LogReason.FRUITSLOT_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, table, afee));
    }

    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setSex(player.getSex());
    seat.setRobot(player.robot());
    seat.setState(SeatState.GAMING);

    // 初始化筹码为金币数
    seat.setTotalChips(convertChip(roomDom, player.getGold()));

    // 更新玩家座位
    dataMgr.updatePlayerSeats(player.getId(), seat);
    // 触发进入桌子事件
    eventMgr.post(new EnterTableEvent(player, seat));

    LOG.info("[水果拉霸]玩家[{}][{}]进入桌子[{}]成功", player.getPlayerName(), player.getId(), table.getId());

    player.curSeat = seat;

    // 桌子玩家列表
    AbstractRoom room = table.room();
    List<MemInfo> mems = new ArrayList<>();
    for (Long playerId : table.players()) {
      mems.add(room.memInfo(playerId));
    }

    return mems;
  }

  /**
   * 快速进入水果拉霸.
   * 
   * @param player 玩家.
   * @param roomId 房间Id.
   */
  public ResMessage fastEnterTable(Player player, int roomId) {

    HuohuaAssert.isTrue(player.curRoom instanceof FruitslotRoom);

    // if (!(player.curRoom instanceof FruitslotRoom)) {
    // LOG.error("[水果拉霸]玩家[{}][{}]当前不在房间中不能直接进入桌子", player.getPlayerName(), player.getId());
    // return;
    // }

    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(seat == null);

    // if (seat != null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
    // return;
    // }

    FruitslotRoom room = (FruitslotRoom) player.curRoom;

    HuohuaAssert.isTrue(room != null && room.getInstanceState().equals(InstanceState.NORMAL),
        ErrorCode.GAME_ROOM_CLOSED);
    // if (room == null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]快速进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
    // return;
    // }
    boolean result = false;
    List<MemInfo> members = null;
    for (FruitslotTable table : room.getTables().values()) {
      if (table.getSeat().getPlayerId() == 0) {
        members = doEnterTable(player, table.getSeat());
        result = true;
        break;
      }
    }

    HuohuaAssert.isTrue(result, ErrorCode.GAME_NOT_SIT);

    // commonMsgMgr.sendTipDlgMsg(player, ErrorCode.GAME_NOT_SIT, Operator.SYSTEM);

    seat = dataMgr.getPlayerSeat(player.getId());
    // 发送快速进入桌子结果失败
    return msgMgr.resFastEnterTableMsg(members, seat.getTotalChips());
  }

  /**
   * 玩家退出牌桌.
   * 
   * @param player 玩家.
   */
  public ResMessage exitTable(Player player) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(seat != null);

    // if (seat == null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]没在牌桌中不能退出牌桌", player.getPlayerName(), player.getId());
    // return;
    // }

    doExitTabl(player, seat);

    // 发送退出房间消息
    return ResMessage.DEFAULT;
  }

  /**
   * 退出桌子.
   * 
   * @param seat 座位.
   */
  private void doExitTabl(Player player, FruitslotSeat seat) {

    dataMgr.removePlayerSeat(seat.getPlayerId());

    player.curSeat = null;

    player.curTable = null;

    LOG.info("[水果拉霸]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.getTable().getId());
    seat.clear();
    // 触发退出桌子事件
    eventMgr.post(new ExitTableEvent(player, seat));


  }

  /**
   * 减少筹码.
   * 
   * @param seat 座位.
   * @param chips 筹码.
   */
  private void minusChips(FruitslotSeat seat, long chips) {
    seat.setTotalChips(seat.getTotalChips() - chips);
    // 发送筹码变化消息
    msgMgr.sendChipsChangeMsg(seat);
  }

  /**
   * 开始水果拉霸 .
   * 
   * @param player 玩家.
   * @param line 压得线.
   * @param bet 每条线的押注.
   */
  public ResMessage startFruitslot(Player player, int line, long bet) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(seat != null);
    // if (seat == null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]不在桌子中不能进行游戏", player.getPlayerName(), player.getId());
    // return;
    // }

    HuohuaAssert.isTrue(!seat.isGaming());

    // if (seat.gaming) {
    // LOG.error("[水果拉霸]玩家[{}][{}]正在游戏中请勿重复开始游戏", player.getPlayerName(), player.getId());
    // return;
    // }

    HuohuaAssert.isTrue(bet > 0 && line > 0 && line <= MAX_LINE);

    // if (bet <= 0 || line <= 0 || line > MAX_LINE) {
    // LOG.error("[水果拉霸]玩家[{}][{}]下注[{}线 *{}筹码]不合法", player.getPlayerName(), player.getId(), line,
    // bet);
    // return;
    // }

    seat.setLine(line);
    seat.setBet(bet);
    seat.setGaming(true);
    // 清空水果篮倍数信息
    seat.setLotteryRewards(null);

    // 合计下注
    long totalBet = 0;
    if (seat.getBonus() == 0) {

      // 线 * 每线押注 .
      totalBet = line * bet;
      // 筹码是否足够
      HuohuaAssert.isTrue(totalBet <= seat.getTotalChips(), ErrorCode.PLAYER_GOLD_NOT_ENOUGH);

      // if (totalBet > seat.totalChips) {
      // LOG.warn("[水果拉霸]玩家[{}][{}]合计下注[{}]超过玩家身上合计筹码[{}]", player.getPlayerName(), player.getId(),
      // totalBet, seat.totalChips);
      // return msgMgr.resFruitslotStartMsg(seat, 1, null);
      // }

      // 扣除押注
      minusChips(seat, totalBet);

      // 扣除押注金币
      playerMgr.minusGold(player, convertGold(seat.getTable().getRoom().getRoomDomain(), totalBet),
          LogReason.FRUITSLOT_BET);
    } else {
      // bonus 免手续费
      seat.setBonus(seat.getBonus() - 1);
    }

    // 随机图标,根据图标计算奖励
    FruitslotReward fruitslotReward = fruitslotControlMgr.randomFruitslotIcons(seat);

    // 增加结算的钱
    seat.setBillChips(seat.getBillChips() + fruitslotReward.getWin());
    // 修改bonus次数

    seat.setBonus(seat.getBonus() + fruitslotReward.getBonus());

    seat.setLotteryRewards(fruitslotReward.getLotteryRewards());

    // for test
    // if (EmptyUtil.listIsEmpty(seat.lotteryRewards)) {
    // seat.lotteryRewards = new ArrayList<>();
    // if (Math.random() > 0.7) {
    // for (int i = 0; i < 3; i++) {
    // Integer multiple = RandCodeUtil.random(31, 48);
    // FruistslotBasket basket = new FruistslotBasket();
    // basket.setMultiple(multiple);
    // basket.setRate(10);
    // seat.lotteryRewards.add(basket);
    // }
    // }
    // }
    // for test

    // 发送公告
    if (fruitslotReward.getBonus() > 0) {
      sendedNotice(seat.getPlayerName(), FruitslotRewardType.BONUS_REWARD,
          fruitslotReward.getBonus());
    }

    // 最终结果输赢筹码
    long resChips = fruitslotReward.getWin() - totalBet;

    long resGold = convertGold(seat.getTable().getRoom().getRoomDomain(), resChips);

    // 数据库输赢日志
    DbLogService
        .log(new Game23WinLoseLog(player, seat, resChips, resGold, LogReason.FRUITSLOT_BET));

    LOG.info("[水果拉霸]玩家[{}][{}]开始水果拉霸,奖励信息{}", player.getPlayerName(), player.getId(),
        JSON.toJSONString(fruitslotReward));

    return msgMgr.resFruitslotStartMsg(seat, fruitslotReward);
  }


  public ResMessage getGameInfo(Player player) {
    return msgMgr.resGameInfo(player);
  }

  /**
   * 如果满足条件发送公告.
   * 
   * @param playerName 玩家名.
   * @param reward 水浒奖励.
   */
  private void sendedNotice(String playerName, FruitslotRewardType type, long chips) {
    // int type = reward.type;
    // long chips = reward.win;
    if (type == FruitslotRewardType.COMMON_REWARD) { // 普通连线奖励 没有公告
      return;
    }

    for (GameNoticeDomainWrapper dom : gameNoticeDic.getGameDoms().get(Game.FRUIT_SLOT.getType())) {
      JSONObject conditionData = dom.conditionData();
      // 公告条件水浒类型
      Integer conditionType = conditionData.getInteger("type");

      if (conditionType == type.getType()) {
        String sendContent = MessageFormat.format(dom.getContent(), playerName, chips);
        if (dom.getMarqueeShow() == BoolInteger.TRUE) { // 跑马灯公告
          noticeMsgMgr.sendMarqueeNoticeMsg(sendContent, NoticeType.GAME, dom.getInterval(),
              dom.getTimes(), dom.getColor());
        }

        if (dom.getChatShow() == BoolInteger.TRUE) { // 聊天公告
          chatMsgMgr.sendSysNoticeMsg(sendContent);
        }
      }
    }
  }

  private void sendMultipleReward(String playerName, long winChips) {
    String sendContent =
        MessageFormat.format("哇！玩家{0}在水果拉霸游戏中开出大奖，一下子赢了{1}！", playerName, winChips);

    noticeMsgMgr.sendMarqueeNoticeMsg(sendContent, NoticeType.GAME, Notice.DEFAULT_INTERVAL,
        Notice.DEFAULT_TIMES, Notice.DEFAULT_COLOR);
  }

  /**
   * 玩家手动结算.
   * 
   * @param player 玩家.
   * @param isExit 是否退出.
   */
  public ResMessage bill(Player player, Integer index, boolean isExit) {
    FruitslotSeat seat = dataMgr.getPlayerSeat(player.getId());

    HuohuaAssert.isTrue(seat != null);

    // if (seat == null) {
    // LOG.error("[水果拉霸]玩家[{}][{}]不在牌桌中不能结算", player.getPlayerName(), player.getId());
    // return;
    // }

    if (!seat.isGaming() && !isExit) {
      LOG.error("[水果拉霸]玩家[{}][{}]还未开始游戏不能结算", player.getPlayerName(), player.getId());
      HuohuaAssert.isTrue(false);
    }

    if (index != null && seat.getBet() > 0 && index >= 0 && index <= 2
        && EmptyUtil.listIsNotEmpty(seat.getLotteryRewards())) {
      long lottery =
          seat.getBet() * seat.getLine() * seat.getLotteryRewards().get(index).getMultiple()
              / seat.getLotteryRewards().get(index).getRate();
      // 发送公告
      sendedNotice(seat.getPlayerName(), FruitslotRewardType.LOTTERY_REWARD, lottery);

      // 清空水果篮倍数信息
      seat.setLotteryRewards(null);

      seat.setBillChips(seat.getBillChips() + lottery);

      fruitslotControlMgr.modifyPrizePoolAll(seat, lottery);
    }

    // 结算
    if (seat.getBillChips() > 0) {
      seat.setTotalChips(seat.getTotalChips() + seat.getBillChips());
      LOG.info("[水果拉霸]玩家[{}][{}]结算筹码[{}]", seat.getPlayerName(), seat.getPlayerId(),
          seat.getBillChips());
    }

    // 结算
    playerMgr.addGold(player,
        convertGold(seat.getTable().getRoom().getRoomDomain(), seat.getBillChips()),
        LogReason.FRUITSLOT_BILL);

    // 新增赢分 计算
    if (seat.getBillChips() > 0) {
      playerMgr.addWinGold(player, seat.getBillChips());
    }

    long totalBet = seat.getLine() * seat.getBet();
    long totalWinChips = seat.getBillChips() - totalBet;

    // 超过倍数通知.
    if (totalWinChips > 0 && totalWinChips / totalBet >= NOTICE_FOR_MULTIPLE) {
      sendMultipleReward(seat.getPlayerName(), totalWinChips);
    }

    msgMgr.sendChipsChangeMsg(seat);
    seat.resetButTotalChips();


    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));

    if (checkClose(seat.getTable())) {
      dataMgr.checkRemoveInstance(seat.getTable().getRoom().getId().intValue(),
          seat.getTable().getRoom().getInstanceId());
    }

    // 发送结算消息
    return ResMessage.DEFAULT;
  }

  /**
   * 清除玩家.
   * 
   * @param playerId 玩家Id.
   */
  private void doClearGameData(long playerId) {
    try {
      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (FruitslotRoom room : dataMgr.allRooms()) {
        room.getPlayers().remove(playerId);

        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (FruitslotTable table : room.getTables().values()) {
          if (table.getSeat().getPlayerId() == playerId) {
            table.getSeat().clear();
            // Player player = playerMgr.getPlayer(playerId);
            // if (player != null) {
            // loginMgr.noticeLogoutForGameExit(player);
            // }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        player.curRoom = null;
        player.curTable = null;
        player.curSeat = null;
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[水果拉霸]清除玩家[" + playerId + "]卡限失败", e);
    }

  }

}
